# 指定文件编码为UTF-8
# coding: utf-8

# 代码执行流程说明：
# 加载MNIST数据集并进行预处理
# 初始化一个两层神经网络
# 选取前3个训练样本作为测试数据
# 分别用两种方法计算梯度：
# numerical_gradient: 使用数值方法计算梯度(准确但慢)
# gradient: 使用反向传播计算梯度(高效)
# 比较两种方法得到的梯度差异
# 输出各层参数(W1,b1,W2,b2)的梯度平均绝对误差
# 注意事项：
# 该脚本主要用于调试和验证反向传播实现
# 实际训练时应使用反向传播梯度
# 差异值大小可以反映反向传播实现的正确性
# 如果差异过大，需要检查反向传播的实现代码

"""
gradient_check.py 功能说明：
1. 实现梯度检查(Gradient Check)功能，用于验证反向传播实现的正确性
2. 比较数值梯度(numerical gradient)和反向传播梯度(backpropagation gradient)的差异
3. 使用MNIST数据集中的前3个样本进行测试
4. 输出各层参数梯度的平均绝对误差

关键点：
- 数值梯度计算准确但计算量大，用于验证反向传播的正确性
- 反向传播梯度计算高效，用于实际训练
- 两种方法计算的梯度应该非常接近
- 差异值可以反映反向传播实现是否正确
"""

# 导入系统模块和操作系统模块
import sys, os
# 添加父目录到系统路径，以便导入父目录中的模块
sys.path.append(os.pardir)

# 导入NumPy数值计算库
import numpy as np
# 从dataset.mnist模块导入MNIST数据加载函数
from dataset.mnist import load_mnist
# 从two_layer_net模块导入TwoLayerNet类
from two_layer_net import TwoLayerNet

# 加载MNIST数据集
# normalize=True: 对图像进行归一化处理(0~1范围)
# one_hot_label=True: 标签采用one-hot编码
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

# 初始化两层神经网络
# 输入层784个神经元(28x28像素)
# 隐藏层50个神经元
# 输出层10个神经元(对应0-9数字分类)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

# 取训练集前3个样本作为测试数据
x_batch = x_train[:3]
t_batch = t_train[:3]

# 计算数值梯度(使用中心差分法)
grad_numerical = network.numerical_gradient(x_batch, t_batch)
# 计算反向传播梯度
grad_backprop = network.gradient(x_batch, t_batch)

# 比较两种梯度计算方法的差异
for key in grad_numerical.keys():
    # 计算平均绝对误差
    diff = np.average(np.abs(grad_backprop[key] - grad_numerical[key]))
    # 打印各层参数的梯度差异
    print(key + ":" + str(diff))

"""
预期输出分析：
W1: 很小的值(如1e-7量级)
b1: 很小的值(如1e-7量级)
W2: 很小的值(如1e-7量级)
b2: 很小的值(如1e-7量级)

说明：
1. 如果差异值都很小(如小于1e-7)，说明反向传播实现正确
2. 如果某个参数的差异值较大，说明对应层的反向传播实现可能有问题
3. 数值梯度是准确的参考值，但计算成本高
4. 反向传播梯度应该与数值梯度非常接近
"""
